package strangeattractor.core;

import java.awt.Image;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Generator {
	//The generator creates the image itself, using a camera and transformation to map the points, and a scheme and renderer to locate them on the image.
	private StandardProjector camera;
	private Transformer transformation;
	private ColorScheme scheme;
	private List<Renderer> renderers = new ArrayList<Renderer>();

	private boolean isRunning = false;
	private boolean cancelled = false;

	public class Runner implements Runnable {

		private int width, height, iterations, updates;
		private ColorScheme scheme;

		public Runner(int width, int height, int iterations, int updates,
				ColorScheme scheme) {
			super();
			this.width = width;
			this.height = height;
			this.iterations = iterations;
			this.updates = updates;
			this.scheme = scheme;
		}

		@Override
		public void run() {
			cancelled = false;
			ZPixelGrid zscreener = new ZPixelGrid(width, height);
			if (updates <= 0) {
				updates = 1;
			}
			Random r = new Random();
			for (int u = 0; u < updates; u++) {
				StrangePoint point = new StrangePoint(r.nextDouble(), r
						.nextDouble(), r.nextDouble());
				for (int i = 0; i < iterations / updates; i++) {
					if (cancelled) {
						break;
					}
					StrangePoint newpoint = new StrangePoint(point,
							transformation.next(point.point));
					if (!((i == 0) || (i + 1) == (iterations / updates))) {
						zscreener.setPixel(scheme.overrides(), scheme
								.getColor(point), camera.getZ(point.point),
								camera.getCoords(point.point));
					}
					point = newpoint;
				}
				for (Renderer renderer : renderers)
					renderer.display(zscreener.getImage());
				if (cancelled) {
					cancelled = false;
					break;
				}
			}
			isRunning = false;
		}

	}

	public Generator(StandardProjector camera, Transformer transformation,
			ColorScheme scheme, Renderer render, Renderer... others) {
		super();
		this.camera = camera;
		this.transformation = transformation;
		this.scheme = scheme;
		this.renderers.add(render);
		for (Renderer rend : others) {
			this.renderers.add(rend);
		}
	}

	public void render(int width, int height, int iterations, int updates) {
		if (!isRunning) {
			isRunning = true;
			new Thread(new Runner(width, height, iterations, updates, scheme))
					.start();
		}
	}

	public void render(int width, int height, int iterations, int updates,
			ColorScheme scheme) {
		if (!isRunning) {
			isRunning = true;
			new Thread(new Runner(width, height, iterations, updates, scheme))
					.start();
		}
	}

	public void rotate(double theta, double phi) {
		this.camera.rotateHorizonal(theta);
		this.camera.rotateVertical(phi);
	}

	public void translate(int x, int y) {
		this.camera.shift(-x, -y);
	}

	public void zoom(double factor) {
		this.camera.zoom(factor);
	}

	public StandardProjector getCamera() {
		return camera;
	}

	public void setCamera(StandardProjector camera) {
		this.camera = camera;
	}

	public Transformer getTransformation() {
		return transformation;
	}

	public void setTransformation(Transformer transformation) {
		this.transformation = transformation;
	}

	public ColorScheme getScheme() {
		return scheme;
	}

	public void setScheme(ColorScheme scheme) {
		this.scheme = scheme;
	}

	public void cancel() {
		this.cancelled = true;
	}

}
